using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Net;
using System.Xml;
using BusinessEntities;
using System.Configuration;
using System.Globalization;

namespace RevRaider.DataAccess
{
    public class ArmoryRepository
    {
        private static string _userAgent = @"Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.2) Gecko/20070219 Firefox/2.0.0.2";

        private static XmlDocument GetData(string xmlUrl)
        {
            try
            {
                HttpWebRequest req = (HttpWebRequest)WebRequest.Create(xmlUrl);
                req.UserAgent = _userAgent;

                HttpWebResponse response = (HttpWebResponse)req.GetResponse();

                XmlDocument xmlDoc = new XmlDocument();
                xmlDoc.Load(response.GetResponseStream());

                //A random 1-3 sec delay to not get blocked by armory server
                System.Threading.Thread.Sleep(1000);

                return xmlDoc;
            }
            catch (System.Net.WebException ex)
            {
                if (ex.Message.CompareTo("The operation has timed out") == 0)
                    return new XmlDocument();
                else
                    throw ex;

            }
        }

        public static Raiders GetAllMembers(SearchCriteria criteria)
        {
            //Declaration
            string url;
            XmlDocument guildData;
            XmlElement root;
            XmlNodeList characterNodes;
            Raiders members;

            //Initialization

            url = FormatURL(criteria);
            guildData = GetData(url);
            root = guildData.DocumentElement;
            characterNodes = root.SelectNodes("//character");
            members = new Raiders();

            ArmoryProgress.Notify(new ArmoryNotificationEventArgs("Successfully retrieved guild info"));

            foreach (XmlNode characterNode in characterNodes)
            {
                Raider raider = ParseRaiders(characterNode, criteria);
                if (raider != null)
                    members.Add(raider);
            }

            return members;
        }

        public static Raiders GetRaiders(SearchCriteria criteria)
        {
            //Declaration
            string url;
            XmlDocument guildData;
            XmlElement root;
            XmlNodeList characterNodes;
            Raiders members;

            //Initialization

            url = FormatURL(criteria);
            guildData = GetData(url);
            root = guildData.DocumentElement;
            characterNodes = root.SelectNodes("//character");
            members = new Raiders();

            ArmoryProgress.Notify(new ArmoryNotificationEventArgs("Successfully retrieved guild info"));

            foreach (XmlNode characterNode in characterNodes)
            {
                Rank rank = Rank.Raider;
                if (characterNode.Attributes["rank"] != null)
                    rank = (Rank)(Convert.ToInt32(characterNode.Attributes["rank"].InnerText));
                if (rank == Rank.GuildMaster || rank == Rank.GM || rank == Rank.Council || rank == Rank.Crafter || rank == Rank.Raider || rank == Rank.Probation) //If GM, officer, crafter, raider or probation
                {
                    Raider raider = ParseRaiders(characterNode, criteria);
                    if (raider != null)
                        members.Add(raider);
                }
            }

            return members;
        }

        private static string FormatURL(SearchCriteria criteria)
        {
            string url, serverName, guildName;
            serverName = criteria.ServerName.Replace(" ", "+");
            guildName = criteria.GuildName.Replace(" ", "+");

            url = String.Format(ConfigurationManager.AppSettings["GuildURL"], serverName, guildName);

            return url;

        }

        public static Raider ParseRaiders(XmlNode characterNode, SearchCriteria criteria)
        {
            //Declaration
            string url;
            XmlDocument characterData;
            XmlElement root;
            Raider raider;
            XmlNode primarySpecNode;
            XmlNode secondarySpecNode;
            XmlNode characterElement;
            XmlNode itemsNode;

            try
            {
                //Initialization and processing
                raider = new Raider();
                if (characterNode.Attributes["name"] != null)
                    raider.Name = characterNode.Attributes["name"].InnerText;

                ArmoryProgress.Notify(new ArmoryNotificationEventArgs(new StringBuilder("Querying [").Append(raider.Name).Append("]").ToString()));

                if (characterNode.Attributes["classId"] != null)
                {
                    raider.ClassId = Convert.ToInt32(characterNode.Attributes["classId"].InnerText);
                    raider.Class = (RaiderClass)(Convert.ToInt32(characterNode.Attributes["classId"].InnerText));
                }
                if (characterNode.Attributes["level"] != null)
                {
                    raider.Level = Convert.ToInt32(characterNode.Attributes["level"].InnerText);
                    if (raider.Level < criteria.MinLevel)
                        return null;

                }
                if (characterNode.Attributes["achPoints"] != null)
                    raider.AchievementPoints = Convert.ToInt32(characterNode.Attributes["achPoints"].InnerText);
                if (characterNode.Attributes["rank"] != null)
                {
                    raider.RankId = Convert.ToInt32(characterNode.Attributes["rank"].InnerText);
                    raider.Rank = (Rank)(Convert.ToInt32(characterNode.Attributes["rank"].InnerText));
                }

                url = new StringBuilder(ConfigurationManager.AppSettings["CharacterURL"]).Append(raider.Name).ToString();
                characterData = GetData(url);
                root = characterData.DocumentElement;

                characterElement = root.SelectSingleNode("characterInfo/character");
                if (characterElement != null)
                {
                    if (characterElement.Attributes["lastModified"] != null)
                    {
                        raider.LastUpdated = Convert.ToDateTime(characterElement.Attributes["lastModified"].InnerText);
                    }
                    if (raider.LastUpdated < criteria.LastUpdated)
                        return null;
                }

                primarySpecNode = root.SelectSingleNode("characterInfo/characterTab/talentSpecs/talentSpec[@group=1]");
                secondarySpecNode = root.SelectSingleNode("characterInfo/characterTab/talentSpecs/talentSpec[@group=2]");

                if (primarySpecNode != null)
                {
                    if (primarySpecNode.Attributes["prim"] != null)
                        raider.PrimarySpec = ParseSpec(primarySpecNode.Attributes["prim"].InnerText);

                }

                if (secondarySpecNode != null)
                {
                    if (primarySpecNode.Attributes["prim"] != null)
                        raider.SecondarySpec = ParseSpec(secondarySpecNode.Attributes["prim"].InnerText);
                }

                raider.Role = DetermineRole(raider.PrimarySpec, raider.Class);

                itemsNode = root.SelectSingleNode("characterInfo/characterTab/items");

                raider.AverageItemLevel = ParseItems(itemsNode);
                if (raider.AverageItemLevel < criteria.MinItemsAvgLevel)
                    return null;

            }
            catch (Exception)
            {
                throw;
            }

            return raider;
        }

        private static Talent ParseSpec(string specText)
        {
            Talent spec;
            if (specText.Equals("Feral Combat") == true)
                spec = Talent.Feral;
            else if (specText.Equals("Beast Mastery") == true)
                spec = Talent.BeastMastery;
            else
                spec = (Talent)Enum.Parse(typeof(Talent), specText, true);

            return spec;
        }

        private static RaiderRole DetermineRole(Talent primarySpec, RaiderClass clas)
        {
            RaiderRole role = RaiderRole.Unknown;

            switch (primarySpec)
            {
                case Talent.None:
                    role = RaiderRole.Unknown;
                    break;
                case Talent.Arms:
                case Talent.Fury:
                case Talent.BeastMastery:
                case Talent.Marksmanship:
                case Talent.Survival:
                case Talent.Assassination:
                case Talent.Combat:
                case Talent.Subtlety:
                case Talent.Shadow:
                case Talent.Enhancement:
                case Talent.Elemental:
                case Talent.Arcane:
                case Talent.Fire:
                case Talent.Affliction:
                case Talent.Demonology:
                case Talent.Destruction:
                case Talent.Balance:
                case Talent.Retribution:
                    role = RaiderRole.DamageDealer;
                    break;
                case Talent.Discipline:
                case Talent.Holy:
                case Talent.Restoration:
                    role = RaiderRole.Healer;
                    break;
                case Talent.Feral:
                case Talent.Protection:
                    role = RaiderRole.Tank;
                    break;
                case Talent.Blood:
                case Talent.Unholy:
                    role = RaiderRole.UselessDK;
                    break;
                case Talent.Frost:
                    if (clas == RaiderClass.Mage)
                        role = RaiderRole.DamageDealer;
                    else
                        role = RaiderRole.UselessDK;
                    break;
                default:
                    role = RaiderRole.Unknown;
                    break;
            }

            return role;
        }

        private static int ParseItems(XmlNode itemsNode)
        {
            if (itemsNode == null)
                return 0;

            int itemsCounter = 0;
            int averageIlevel = 0;
            int iLevelTotal = 0;
            try
            {
                foreach (XmlNode itemNode in itemsNode.ChildNodes)
                {
                    if (itemNode.Attributes["slot"] != null)
                    {
                        if (itemNode.Attributes["slot"].InnerText.Equals("3") == false
                            && itemNode.Attributes["slot"].InnerText.Equals("18") == false
                            && itemNode.Attributes["slot"].InnerText.Equals("-1") == false)
                        {
                            if (itemNode.Attributes["level"] != null)
                                iLevelTotal += Convert.ToInt32(itemNode.Attributes["level"].InnerText);
                            itemsCounter++;
                        }
                    }
                }

                if (itemsCounter > 0)
                {
                    averageIlevel = iLevelTotal / itemsCounter;
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }

            return averageIlevel;
        }
    }
}
